package com.nlx.notes.support.helper.aop;

import com.nlx.notes.configuer.redis.annotation.RedisLock;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE) // 优先级最高
public class RedisLockAspect {

    @Autowired
    private RedissonClient redissonClient;

    private final ExpressionParser parser = new SpelExpressionParser();

    @Around("@annotation(com.nlx.notes.configuer.redis.annotation.RedisLock)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("出发了aop拦截");
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        RedisLock redisLock = method.getAnnotation(RedisLock.class);
        // 解析 SpEL 表达式，获取 lockKey
        String lockKey = parseSpEL(redisLock.key(), signature, joinPoint.getArgs());
        RLock lock = redissonClient.getLock("lock:" + lockKey);

        boolean locked = false;
        try {
            locked = lock.tryLock(redisLock.waitTime(), redisLock.leaseTime(), redisLock.unit());
            if (locked) {
                Object proceed = joinPoint.proceed();
                if (TransactionSynchronizationManager.isActualTransactionActive()) {
                    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                        @Override
                        public void afterCommit() {
                            try {
                                if (lock.isHeldByCurrentThread()) {
                                    lock.unlock();
                                    log.info("事务提交后释放分布式锁：{}", lock.getName());
                                }
                            } catch (Exception e) {
                                log.error("事务后释放锁异常", e);
                            }
                        }
                    });
                }
                return proceed; // 执行原方法
            } else {
                throw new RuntimeException("获取分布式锁失败，请稍后重试");
            }
        } finally {
            if (!TransactionSynchronizationManager.isActualTransactionActive()) {
                if (locked && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
    }

    private String parseSpEL(String spEl, MethodSignature signature, Object[] args) {
        StandardEvaluationContext context = new StandardEvaluationContext();
        String[] paramNames = signature.getParameterNames();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }
        return parser.parseExpression(spEl).getValue(context, String.class);
    }

}
